use std::fs;
use std::io::Read;
use crate::cli::TextSignOpts;
use crate::cli::TextSignFormat;
use anyhow::Result;
use base64::Engine;
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
use ed25519_dalek::{SecretKey, Signature, Signer, SigningKey, Verifier, VerifyingKey};

use crate::get_reader;

trait TextSign {
    //dyn 动态分派
    fn sign(&self, reader: &mut dyn Read) -> Result<Vec<u8>>;
}

trait TextVerify {
    // 泛型 静态分派
    fn verify<R: Read>(&self, reader: &mut R, sig: &[u8]) -> Result<bool>;
}

#[derive(Debug, Copy, Clone)]
struct Blake3s {
    key: [u8; 32],
}

#[derive(Debug)]
struct Ed25519Signer {
    key: SigningKey,
}

#[derive(Debug)]
struct Ed25519Verifier {
    key: VerifyingKey,
}

impl TextSign for Blake3s {
    fn sign(&self, reader: &mut dyn Read) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        let hash = blake3::hash(&buf);
        let hash = hash.as_bytes().to_vec();
        Ok(hash)
    }
}

impl TextVerify for Blake3s {
    fn verify<R: Read>(&self, reader: &mut R, sig: &[u8]) -> Result<bool> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        let hash = blake3::hash(&buf);
        let hash = hash.as_bytes();
        Ok(hash == sig)
    }
}

impl TextSign for Ed25519Signer {
    fn sign(&self, reader: &mut dyn Read) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        let sig = self.key.sign(&buf);
        Ok(sig.to_bytes().to_vec())
    }
}

impl TextVerify for Ed25519Verifier {
    fn verify<R: Read>(&self, reader: &mut R, sig: &[u8]) -> Result<bool> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        let sig = Signature::from_bytes(sig.try_into()?);
        Ok(self.key.verify(&buf, &sig).is_ok())
    }
}


pub fn sign(data: &TextSignOpts) -> Result<()> {
    let mut reader = get_reader(&data.input)?;
    let signed = match &data.format {
        TextSignFormat::Blake3 => {
            //println!("{}",&data.key);
            let key = fs::read(&data.key)?;
            let key = &key[..32];
            let key = key.try_into()?;
            let signer: Blake3s = Blake3s { key };
            signer.sign(&mut reader)?
        }
        TextSignFormat::Ed25519 => {
            let key = fs::read(&data.key)?;
            let key = &key[..32];
            let key = key.try_into()?;
            let key = SigningKey::from_bytes(key);
            let sign: Ed25519Signer = Ed25519Signer { key };
            sign.sign(&mut reader)?
        }
    };
    let data = BASE64_URL_SAFE_NO_PAD.encode(signed);
    println!("{},{}", &data, &data.len());
    Ok(())
}